﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using CanadaPost.Shipping;
using CanadaPost.Shipping.Data;
using Medianamik.Core;
using Medianamik.UI.Web.Shop.Data;
using Medianamik.UI.Web.Shop.Data.Dto;
using Medianamik.UI.Web.Shop.Data.Interfaces;
using Medianamik.UI.Web.Shop.Data.Repositories;
using Medianamik.UI.Web.Shop.Services;
using IAddress = Medianamik.UI.Web.Shop.Data.Interfaces.IAddress;
using IClientProfile = Medianamik.UI.Web.Shop.Data.Interfaces.IClientProfile;
using IModel = Medianamik.UI.Web.Shop.Data.Interfaces.IModel;
using IProduct = Medianamik.UI.Web.Shop.Data.Interfaces.IProduct;
using IRegion = Medianamik.UI.Web.Shop.Data.Interfaces.IRegion;

namespace Medianamik.UI.Web.Shop.Services
{
    public interface ICanadaPostService : IShippingService
    {
        double TotalPrice { get; }
        IList<CanadaPostItem> CanadaPostItems { get; }
        CanadaPostCredentials CanadaPostCredentials { get; }
        CanadaPostAddress CanadaPostAddress { get; }
    }

    public class CanadaPostService<TShoppingCart, TShoppingCartItem, TProduct, TModel,
                                   TShoppingCartItemWithModels, TRegion, TClientProfile, TAddress> : ICanadaPostService
        where TShoppingCart : IShoppingCart<TShoppingCartItem, TModel>
        where TShoppingCartItem : IShoppingCartItem<TModel>
        where TProduct : IProduct
        where TModel : IModel
        where TShoppingCartItemWithModels : IShoppingCartItemWithModels<TModel>
        where TRegion : IRegion
        where TClientProfile : IClientProfile
        where TAddress : IAddress

    {
        private readonly IAddressRepository<TAddress> _addressRepository;
        private readonly IClientProfileRepository<TClientProfile> _clientProfileRepository;
        private readonly IPostCanadaRepository _postCanadaRepository;
        private readonly IRegionRepository<TRegion> _regionRepository;

        private readonly
            IShoppingCartService<TShoppingCart, TShoppingCartItem, TProduct, TModel, TShoppingCartItemWithModels>
            _shoppingCartService;

        private TShoppingCart _currentShoppingCart;

        public CanadaPostService(IClientProfileRepository<TClientProfile> clientProfileRepository,
                                 IAddressRepository<TAddress> addressRepository,
                                 IPostCanadaRepository postCanadaRepository,
                                 IShoppingCartService
                                     <TShoppingCart, TShoppingCartItem, TProduct, TModel, TShoppingCartItemWithModels>
                                     shoppingCartService,
                                 IRegionRepository<TRegion> regionRepository)
        {
            _clientProfileRepository = clientProfileRepository;
            _addressRepository = addressRepository;
            _postCanadaRepository = postCanadaRepository;
            _shoppingCartService = shoppingCartService;
            _regionRepository = regionRepository;
        }

        private TShoppingCart CurrentShoppingCart
        {
            get
            {
                if (_currentShoppingCart == null)
                {
                    _currentShoppingCart = _shoppingCartService.CurrentShoppingCart;
                }

                return _currentShoppingCart;
            }
        }

        #region ICanadaPostService Members

        public double CalculateShippingPrice()
        {
            double deliveryCost = -1;

            try
            {
                deliveryCost = StandardShipping.GetStandardDeliveryCost(CanadaPostItems, TotalPrice,
                                                                        CanadaPostCredentials, CanadaPostAddress);
            }
            catch (Exception e)
            {
                ManageError(e);
            }

            return deliveryCost;
        }

        public virtual string Name { get; set; }
        public virtual Guid Id { get; set; }

        public double TotalPrice
        {
            get { return _shoppingCartService.GetShoppingCartItemsTotalPrice(); }
        }

        #endregion

        #region Client Delivery Address

        private CanadaPostAddress _postCanadaAddress;

        public CanadaPostAddress CanadaPostAddress
        {
            get { return _postCanadaAddress ?? (_postCanadaAddress = GetClientDeliveryAddress()); }
        }

        private CanadaPostAddress GetClientDeliveryAddress()
        {
            var deliveryAddress = new CanadaPostAddress();
            Guid clientId = CurrentShoppingCart.Client.GetValueOrDefault();

            if (clientId != Guid.Empty)
            {
                TClientProfile clientProfile = _clientProfileRepository.Get(clientId);

                deliveryAddress = ClientProfileToPostCanadaAddressDto(clientProfile);
            }
            return deliveryAddress;
        }

        private CanadaPostAddress ClientProfileToPostCanadaAddressDto(TClientProfile clientProfile)
        {
            var postCanadaAddress = new CanadaPostAddress();
            if (clientProfile != null)
            {
                TAddress paymentAddress = _addressRepository.Get(clientProfile.PaymentAddress.GetValueOrDefault(),
                                                                 CultureInfo.InvariantCulture, ContentState.Draft);

                if (paymentAddress != null)
                {
                    TRegion state = _regionRepository.Get(paymentAddress.State.GetValueOrDefault());
                    TRegion country = _regionRepository.Get(paymentAddress.Country.GetValueOrDefault());
                    postCanadaAddress.City = paymentAddress.City;
                    postCanadaAddress.State = state.Name;
                    postCanadaAddress.Country = country.Name;
                    postCanadaAddress.PostalCode = paymentAddress.ZipCode;
                }
            }
            return postCanadaAddress;
        }

        #endregion

        #region Post Canada Credentials

        private CanadaPostCredentials _postCanadaCredentials;

        public CanadaPostCredentials CanadaPostCredentials
        {
            get { return _postCanadaCredentials ?? (_postCanadaCredentials = GetPostCanadaCredentials()); }
        }

        private CanadaPostCredentials GetPostCanadaCredentials()
        {
            var postCanadaCredentials = new CanadaPostCredentials();

            PostCanada postCanada = _postCanadaRepository.Get();

            if (postCanada != null)
            {
                postCanadaCredentials.MerchantKey = postCanada.MerchantKey;
                postCanadaCredentials.FromPostalCode = postCanada.FromPostalCode;
                postCanadaCredentials.ShippingProviderUrl = postCanada.UrlToPost;
            }

            return postCanadaCredentials;
        }

        #endregion

        #region Shopping Cart Items

        private IList<CanadaPostItem> _canadaPostItems;

        public IList<CanadaPostItem> CanadaPostItems
        {
            get { return _canadaPostItems ?? (_canadaPostItems = GetShoppingCartItems()); }
        }

        private IList<CanadaPostItem> GetShoppingCartItems()
        {
            var postCanadaItems = new List<CanadaPostItem>();
            IList<TShoppingCartItemWithModels> shoppingCartItems = _shoppingCartService.GetShoppingCartItems();

            if (shoppingCartItems != null && shoppingCartItems.Count > 0)
            {
                postCanadaItems.AddRange(shoppingCartItems
                                             .Select(
                                                 shoppingCartItem => ShoppingCartItemToCanadaPostItem(shoppingCartItem)));
            }
            return postCanadaItems;
        }

        private CanadaPostItem ShoppingCartItemToCanadaPostItem(TShoppingCartItemWithModels shoppingCartItem)
        {
            var postCanadaItem = new CanadaPostItem();

            if (shoppingCartItem != null)
            {
                postCanadaItem.Quantity = shoppingCartItem.Quantity.Value;
                postCanadaItem.Name = shoppingCartItem.Name;
                postCanadaItem.Height = shoppingCartItem.Height;
                postCanadaItem.Length = shoppingCartItem.Length;
                postCanadaItem.Weight = shoppingCartItem.Weight;
                postCanadaItem.Width = shoppingCartItem.Width;
                postCanadaItem.Name = shoppingCartItem.Name;

                if (shoppingCartItem.Models.Count > 0)
                {
                    TModel selectedModel = shoppingCartItem.Models.First();

                    if (selectedModel.Height.GetValueOrDefault(0) > 0)
                    {
                        postCanadaItem.Height = selectedModel.Height.Value;
                    }

                    if (selectedModel.Length.GetValueOrDefault(0) > 0)
                    {
                        postCanadaItem.Length = selectedModel.Length.Value;
                    }

                    if (selectedModel.Weight.GetValueOrDefault(0) > 0)
                    {
                        postCanadaItem.Weight = selectedModel.Weight.Value;
                    }

                    if (selectedModel.Width.GetValueOrDefault(0) > 0)
                    {
                        postCanadaItem.Width = selectedModel.Width.Value;
                    }
                }
            }

            return postCanadaItem;
        }

        #endregion

        protected virtual void ManageError(Exception e)
        {
        }
    }

    public class CanadaPostService : CanadaPostService<ShoppingCart, ShoppingCartItem, BaseProduct, Model,
                                         ShoppingCartItemWithModels, Region, ClientProfile, Address>
    {
        public CanadaPostService(IClientProfileRepository<ClientProfile> clientProfileRepository,
                                 IAddressRepository<Address> addressRepository,
                                 IPostCanadaRepository postCanadaRepository,
                                 IShoppingCartService<ShoppingCart,
                                     ShoppingCartItem, BaseProduct, Model, ShoppingCartItemWithModels> shoppingCartService,
                                 IRegionRepository<Region> regionRepository) :
                                     base(
                                     clientProfileRepository, addressRepository, postCanadaRepository,
                                     shoppingCartService, regionRepository)
        {
        }
    }
}